package cn.migu.music.hangupsms.service;

import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;

import cn.migu.music.cljz.common.ReturnCode;
import cn.migu.music.cljz.common.dataobject.BaseRsp;
import cn.migu.music.common.kafka.MiguKafkaTemplate;
import cn.migu.music.common.redis.IRedisClient;
import cn.migu.music.common.utils.DateTimeConvertor;
import cn.migu.music.common.utils.LogUtils;
import cn.migu.music.hangupsms.common.config.HangUpSmsConfig;
import cn.migu.music.hangupsms.common.entity.AddBlackUserReq;
import cn.migu.music.hangupsms.common.entity.BillInfo;
import cn.migu.music.hangupsms.common.entity.ViewVrbtSettings;
import cn.migu.music.hangupsms.common.entity.VrbtSendMsg;
import cn.migu.music.hangupsms.constants.EViewSmsType;
import cn.migu.music.hangupsms.model.CLSmsSendReq;
import cn.migu.music.hangupsms.model.CLSmsSendRsp;
import cn.migu.music.hangupsms.model.ClSmsSettingParam;
import cn.migu.music.hangupsms.model.ViewVrbtCLCache;
import cn.migu.music.hangupsms.model.ViewVrbtSendMsgResult;
import cn.migu.music.hangupsms.remote.CLSmsFeignClient;
import cn.migu.music.hangupsms.remote.PhoneCodeService;
import cn.migu.music.hangupsms.remote.SMSFeignService;
import cn.migu.music.hangupsms.repository.ViewVrbtSettingsMapper;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class ViewVrbtSendMsgService {

    private static final String    VIEW_VRBT_SETTING_CACHE_HEAD = "view-vrbt-setting:";
    private static final String    VIEW_VRBT_SEND_CACHE_HEAD    = "view-vrbt-has-send:";
    private static final String    VIEW_VRBT_CL_SEND_CACHE_HEAD = "view-vrbt-cl-send:";
    private static final String    NULL_STR                     = "null";
    private static final String    VALUE_CODE                   = "1";
    /**
     * 全国
     */
    private static final String    NATIONWIDE                   = "00";
    private static final String    HH_MM_SS                     = "HH:mm:ss";
    private static final String    VIEW_SEND_MSG_LOCK           = "view-send-msg-lock:";
    private static final String    IMMEDIATELY_SEND             = "1";

    @Autowired
    private ViewVrbtSettingsMapper viewVrbtSettingsMapper;

    @Autowired
    private PhoneCodeService       phoneCodeService;
    @Autowired
    private SMSFeignService        smsFeignService;
    @Autowired
    private HangUpSmsConfig        hangUpSmsConfig;
    @Autowired
    private MiguKafkaTemplate      miguKafkaTemplate;
    @Autowired
    private IRedisClient           redisClient;
    @Autowired
    private HangUpService          hangUpService;

    @Autowired
    private CLSmsFeignClient       clSmsFeignClient;
    @Autowired
    private CeLueShiService        ceLueShiService;

    @Async("hangupViewVrbtAsyncExecutor")
    @SuppressWarnings("all")
    public void contentProcess(BillInfo billInfo) {
        if (!sendBeforeCheck(billInfo)) {
            // 不满足发短信条件
            return;
        }

        ViewVrbtSettings viewVrbtSettings = getViewVrbtSettingsFromCache(billInfo.getToneId());
        if (null != viewVrbtSettings) {
            String provinceId = phoneCodeService.getProvinceId(billInfo.getCallerPhone());
            if (StringUtils.isNotEmpty(viewVrbtSettings.getProvinces()) &&
                StringUtils.isNotEmpty(provinceId) &&
                (provinceId.matches(viewVrbtSettings.getProvinces()) ||
                 viewVrbtSettings.getProvinces().contains(NATIONWIDE))) {
                // 发短信，获取锁
                String lockKey = getLockKey(billInfo);
                if (!tryLock(lockKey)) {
                    // 加锁失败
                    LogUtils.info(log, "获取锁失败,观看视频彩铃:toneId:%s,msisdn:%s", billInfo.getToneId(), billInfo.getCallerPhone());
                    return;
                }
                // 加锁成功，发短信
                sendMsg(billInfo, viewVrbtSettings, provinceId);
                // 释放锁
                unLock(lockKey);
            }
        }
    }

    private String getLockKey(BillInfo billInfo) {
        return VIEW_SEND_MSG_LOCK + billInfo.getToneId() + ":" + billInfo.getCallerPhone();
    }

    private boolean tryLock(String lockKey) {
        try {
            long expires = System.currentTimeMillis() + 60000L;
            String expiresStr = String.valueOf(expires);
            if (Boolean.TRUE.equals(redisClient.setnx(lockKey, expiresStr))) {
                return true;
            } else {
                String currentValueStr = redisClient.get(lockKey);
                if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                    // 加锁
                    String oldValue = redisClient.getset(lockKey, expiresStr);
                    return StringUtils.equals(oldValue, currentValueStr);
                }

                return false;
            }
        } catch (Exception ex) {
            LogUtils.error(ex, log, "加锁异常,key:%s", lockKey);
            return false;
        }
    }

    private void unLock(String lockKey) {
        try {
            redisClient.del(lockKey);
        } catch (Exception ex) {
            LogUtils.error(ex, log, "解锁异常,key:%s", lockKey);
        }
    }

    /**
     * 校验发短信前置条件
     * 
     * @param billInfo
     * @return
     */
    private boolean sendBeforeCheck(BillInfo billInfo) {
        try {
            LocalTime now = LocalTime.now();
            String[] timeLimit = StringUtils.split(hangUpSmsConfig.getViewVrbtCanSendMsgTimeLimit(), "|");
            String start = timeLimit[0];
            String end = timeLimit[1];
            LocalTime startTime = LocalTime.parse(start, DateTimeFormatter.ofPattern(HH_MM_SS));
            LocalTime endTime = LocalTime.parse(end, DateTimeFormatter.ofPattern(HH_MM_SS));
            if (now.isBefore(startTime) || now.isAfter(endTime)) {
                // 当前时间不在发短信时间段
                return false;
            }

            // 话单内容错误 或 挂机黑名单用户，则不发短信
            return billInfo.isCanSend() && !isBlackHangupUser(billInfo.getCallerPhone());
        } catch (Exception ex) {
            LogUtils.error(ex, log, "条件校验异常");
        }
        return false;
    }

    private boolean isBlackHangupUser(String phone) {
        AddBlackUserReq addBlackUserReq = new AddBlackUserReq();
        addBlackUserReq.setMsisdn(phone);
        addBlackUserReq.setNeedRefresh("0");
        return Boolean.TRUE.equals(hangUpService.queryBlackUser(addBlackUserReq));
    }

    /**
     * 查询配置，先从redis取，取不到再从数据库取
     *
     * @param toneId
     * @return 配置
     */
    private ViewVrbtSettings getViewVrbtSettingsFromCache(String toneId) {
        String value = redisClient.get(getCacheKey(toneId));
        if (StringUtils.equals(value, NULL_STR)) {
            // null缓存
            return null;
        }
        if (StringUtils.isNotEmpty(value)) {
            return JSON.parseObject(value, ViewVrbtSettings.class);
        }

        List<ViewVrbtSettings> dataList = viewVrbtSettingsMapper.getByToneId(toneId);
        if (CollectionUtils.isNotEmpty(dataList)) {
            ViewVrbtSettings data = dataList.get(0);
            redisClient.setex(getCacheKey(toneId), JSON.toJSONString(data), hangUpSmsConfig.getViewVrbtSettingCacheSeconds());
            return data;
        }
        // 配置不存在，在redis缓存10秒，避免大量查库
        redisClient.setex(getCacheKey(toneId), NULL_STR, 10);

        return null;
    }

    private String getCacheKey(String toneId) {
        return VIEW_VRBT_SETTING_CACHE_HEAD + toneId;
    }

    public void deleteSettingCacheByToneId(String toneId) {
        redisClient.del(getCacheKey(toneId));
    }

    private String getKeyForHasSend(String toneId, String phone) {
        return VIEW_VRBT_SEND_CACHE_HEAD + toneId + ":" + phone;
    }

    private void sendMsg(BillInfo billInfo, ViewVrbtSettings viewVrbtSettings, String provinceId) {
        String hasSendKey = getKeyForHasSend(billInfo.getToneId(), billInfo.getCallerPhone());
        if (hasSendMsg(hasSendKey)) {
            // 已发过短信，直接返回
            return;
        }

        try {
            sendMsgBySmsType(billInfo, viewVrbtSettings, provinceId);
        } catch (Exception ex) {
            LogUtils.error(ex, log, "发观看视频彩铃挂机短信失败");
            sendKafkaForResult(billInfo.getToneId(), billInfo.getCallerPhone(), billInfo.getEndTime(), false, viewVrbtSettings.getCallingMsg());
        }
    }

    private void sendMsgBySmsType(BillInfo billInfo, ViewVrbtSettings viewVrbtSettings, String provinceId) {
        if (StringUtils.equalsAny(viewVrbtSettings.getMsgPort(), EViewSmsType.CL_SMS.getCode())) {
            // 创蓝发文本短信
            sendCLSms(billInfo, viewVrbtSettings);
        } else if (StringUtils.equalsAny(viewVrbtSettings.getMsgPort(), EViewSmsType.CL_VMS.getCode())) {
            // 创蓝发视频短信
            sendCLVms(billInfo, viewVrbtSettings);
        } else {
            sendJzSms(billInfo, viewVrbtSettings, provinceId);
        }
    }

    private void sendJzSms(BillInfo billInfo, ViewVrbtSettings viewVrbtSettings, String provinceId) {
        String msg = viewVrbtSettings.getCallingMsg();
        String templateId = "hangup_view_vrbt_msg";
        BaseRsp baseRsp = null;
        String[] timeLimit = StringUtils.split(hangUpSmsConfig.getViewVrbtCanSendMsgTimeLimit(), "|");
        Date endTime = DateTimeConvertor.parse(timeLimit[1], HH_MM_SS);
        CeLueShiService.SmsSendResult smsSendResult = ceLueShiService.consumedByCeLueShi(msg, provinceId, billInfo.getCallerPhone(), templateId,
            endTime.getTime() / 1000);
        if (smsSendResult.getConsumed()) {
            baseRsp = smsSendResult.getBaseRsp();
        } else {
            msg = smsSendResult.getMsg();
            VrbtSendMsg event = new VrbtSendMsg();
            event.setMsisdn(billInfo.getCallerPhone());
            event.setMsgPort(viewVrbtSettings.getMsgPort());
            event.setMsgContent(msg);
            event.setAccessPlatformID(BaseService.ACCESS_PLATFORM_ID);
            event.setDid(hangUpSmsConfig.getSendDid());
            event.setSeq(BaseService.generateSeq(hangUpSmsConfig.getSendDid()));
            event.setDidpwd(BaseService.generateMD5String(event.getSeq() + hangUpSmsConfig.getSendDidPwd()));
            event.setProvinceId(provinceId);
            event.setMsgtemplateId(templateId);
            baseRsp = smsFeignService.vRBTSendMsgV3(event);
        }

        boolean result = baseRsp != null && StringUtils.equals(baseRsp.getCode(), ReturnCode.success.getCode());
        // 结果发送kafka通知宣发平台
        sendKafkaForResult(billInfo.getToneId(), billInfo.getCallerPhone(), billInfo.getEndTime(), result, viewVrbtSettings.getCallingMsg());
        // 发送成功，缓存redis记录
        String hasSendKey = getKeyForHasSend(billInfo.getToneId(), billInfo.getCallerPhone());
        cacheSendRecord(hasSendKey, result);
    }

    private void sendCLVms(BillInfo billInfo, ViewVrbtSettings viewVrbtSettings) {
        boolean result = true;
        String batchId = null;
        ClSmsSettingParam clSmsSettingParam = JSON.parseObject(viewVrbtSettings.getCallingMsg(), ClSmsSettingParam.class);
        CLSmsSendReq clSmsSendReq2 = new CLSmsSendReq();
        clSmsSendReq2.setMobiles(billInfo.getCallerPhone());
        clSmsSendReq2.setTemplateId(clSmsSettingParam.getTemplateId());
        clSmsSendReq2.setBusinessId(clSmsSettingParam.getBusinessId());
        CLSmsSendRsp clSmsSendRsp2 = clSmsFeignClient.sendVms(clSmsSendReq2);
        if (StringUtils.equals(ReturnCode.success.getCode(), clSmsSendRsp2.getCode())) {
            batchId = getBatchIdFromResp(clSmsSendRsp2);
        } else {
            result = false;
        }
        if (result) {
            // 缓存创蓝发短信记录，用于等待回执结果
            cacheCLSendParam(batchId, billInfo.getToneId(), billInfo.getEndTime(), viewVrbtSettings.getCallingMsg());
        } else {
            // 结果发送kafka通知宣发平台
            sendKafkaForResult(billInfo.getToneId(), billInfo.getCallerPhone(), billInfo.getEndTime(), false, viewVrbtSettings.getCallingMsg());
        }
    }

    private void sendCLSms(BillInfo billInfo, ViewVrbtSettings viewVrbtSettings) {
        boolean result = true;
        String batchId = null;
        ClSmsSettingParam clSmsSettingParam = JSON.parseObject(viewVrbtSettings.getCallingMsg(), ClSmsSettingParam.class);
        CLSmsSendReq clSmsSendReq1 = new CLSmsSendReq();
        clSmsSendReq1.setMobiles(billInfo.getCallerPhone());
        clSmsSendReq1.setTemplateId(clSmsSettingParam.getTemplateId());
        clSmsSendReq1.setBusinessId(clSmsSettingParam.getBusinessId());
        clSmsSendReq1.setSendingType(IMMEDIATELY_SEND);
        CLSmsSendRsp clSmsSendRsp1 = clSmsFeignClient.sendSms(clSmsSendReq1);
        if (StringUtils.equals(ReturnCode.success.getCode(), clSmsSendRsp1.getCode())) {
            batchId = getBatchIdFromResp(clSmsSendRsp1);
        } else {
            result = false;
        }
        if (result) {
            // 缓存创蓝发短信记录，用于等待回执结果
            cacheCLSendParam(batchId, billInfo.getToneId(), billInfo.getEndTime(), viewVrbtSettings.getCallingMsg());
        } else {
            // 结果发送kafka通知宣发平台
            sendKafkaForResult(billInfo.getToneId(), billInfo.getCallerPhone(), billInfo.getEndTime(), false, viewVrbtSettings.getCallingMsg());
        }
    }

    private String getBatchIdFromResp(CLSmsSendRsp clSmsSendRsp1) {
        return clSmsSendRsp1.getData() == null ? null : clSmsSendRsp1.getData().getBatchId();
    }

    private boolean hasSendMsg(String hasSendKey) {
        try {
            String sendValue = redisClient.get(hasSendKey);
            // 已经发过短信了
            return StringUtils.isNotEmpty(sendValue);
        } catch (Exception ex) {
            LogUtils.error(ex, log, "查询是否发过短信异常");
            return false;
        }
    }

    private void cacheSendRecord(String hasSendKey, boolean result) {
        try {
            if (result) {
                redisClient.setex(hasSendKey, VALUE_CODE, hangUpSmsConfig.getViewVrbtHasSendCacheSeconds());
            }
        } catch (Exception ex) {
            LogUtils.error(ex, log, "缓存发送短信记录异常");
        }
    }

    public void cacheSendRecordByMsisdn(String toneId, String callerPhone, boolean result) {
        String hasSendKey = getKeyForHasSend(toneId, callerPhone);
        cacheSendRecord(hasSendKey, result);
    }

    private void cacheCLSendParam(String batchId, String toneId, String endTime, String msg) {
        if (StringUtils.isEmpty(batchId)) {
            return;
        }
        String key = VIEW_VRBT_CL_SEND_CACHE_HEAD + batchId;
        ViewVrbtCLCache viewVrbtCLCache = new ViewVrbtCLCache();
        viewVrbtCLCache.setToneId(toneId);
        viewVrbtCLCache.setEndTime(endTime);
        viewVrbtCLCache.setMsg(msg);
        redisClient.setex(key, JSON.toJSONString(viewVrbtCLCache), hangUpSmsConfig.getViewVrbtCLSendCacheSeconds());
    }

    public ViewVrbtCLCache getCacheCLSendParam(String batchId) {
        try {
            String key = VIEW_VRBT_CL_SEND_CACHE_HEAD + batchId;
            String value = redisClient.get(key);
            if (StringUtils.isNotEmpty(value)) {
                return JSON.parseObject(value, ViewVrbtCLCache.class);
            }
        } catch (Exception ex) {
            LogUtils.error(ex, log, "获取创蓝发短信redis缓存参数失败");
        }
        return null;
    }

    /**
     * 通知宣发平台发短信结果
     *
     * @param toneId
     * @param callerPhone
     * @param billEndTime
     * @param result
     * @param msg
     */
    public void sendKafkaForResult(String toneId, String callerPhone, String billEndTime, boolean result, String msg) {
        try {
            ViewVrbtSendMsgResult event = new ViewVrbtSendMsgResult();
            event.setToneId(toneId);
            event.setPhone(callerPhone);
            event.setCallTime(billEndTime);
            event.setMsg(msg);
            event.setSendMsgResult(result);
            miguKafkaTemplate.send(hangUpSmsConfig.getViewVrbtNotifyXFTopic(), JSON.toJSONString(event));
        } catch (Exception ex) {
            LogUtils.error(ex, log, "回执宣发失败: toneId:%s,phone:%s,result:%s", toneId, callerPhone, String.valueOf(result));
        }
    }
}
